#!/usr/bin/ruby
################################################################################
#
#	VirtualTwitter
#
#	"Twitter search client for GNOME"
#
#	Version 0.3
#
#	Copyright (C) [2009] [Jan Hofmann <elusive@virtualpixel.de>]
#
#	This program is free software; you can redistribute it and/or modify it
#	under the terms of the GNU General Public License as published by the Free
#	Software Foundation; either version 3 of the License, or (at your option)
#	any later version.
#
#	This program is distributed in the hope that it will be useful, but WITHOUT
#	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
#	FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
#	more details.
#
#	You should have received a copy of the GNU General Public License along with
#	this program; if not, see <http://www.gnu.org/licenses/>.
#
################################################################################

########## requirements ########################################################
require 'gtk2'
require 'rubygems'
require 'twitter'
require 'net/https'
require 'net/http'
require 'readline'
require 'RNotify'
require 'launchy'
require 'fileutils'

########## checking configuration ##############################################
rootPath="#{ENV['HOME']}/.config/virtualtwitter"

if File.directory? rootPath
  puts "~/.config/virtualtwitter directory found!"
else
  puts "~/.config/virtualtwitter directory not found! Will create it..."
    FileUtils.mkdir_p rootPath
  puts "...done!"
end

if File.exists?("#{rootPath}/searchterms.conf")
    puts "Configuration file found!"
  if File.zero?("#{rootPath}/searchterms.conf")
    puts "#####################################################"
    puts "WARNING:"
    puts "Configuration file is empty!"
    puts "You have to write your searchterms in"
    puts "~/.config/virtualtwitter/searchterms.conf"
    puts "You may look into searchterms.conf.examples"
    puts "#####################################################"
    exit
  end
else
  puts "#####################################################"
  puts "WARNING:"
  puts "No configuration file found!"
  puts "Please create searchterms.conf in"
  puts "~/.config/virtualtwitter"
  puts "You may look into searchterms.conf.examples"
  puts "#####################################################"
  exit
end

profilePicturesPath="#{rootPath}/profile_pictures/"
puts profilePicturesPath
if File.directory? profilePicturesPath
  puts "Found directory for profile pictures!"
  puts "delete old pictures..."
  FileUtils.rm_rf profilePicturesPath
  FileUtils.mkdir_p profilePicturesPath
  puts "...done!"
else
  puts "Creating directory for profile pictures..."
    FileUtils.mkdir_p profilePicturesPath
  puts "...done!"
end

########## status icon #########################################################
tray = Gtk::StatusIcon.new
icon = Gdk::Pixbuf.new("/opt/virtualtwitter/virtualtwitter.png",22,22)

tray.pixbuf=(icon)
tray.tooltip=("VirtualTwitter - Twitter search client for GNOME")
tray.visible=(true)

########## about box ###########################################################
dialog = Gtk::AboutDialog.new
logo = Gdk::Pixbuf.new("/opt/virtualtwitter/virtualtwitter.png")
dialog.logo = logo
dialog.name = "About VirtualTwitter"
dialog.version = "0.3b1"
dialog.copyright = "(C) 2009 Jan Hofmann"
dialog.comments = "VirtualTwitter - Twitter search client for GNOME"
dialog.website = "http://code.google.com/p/virtualtwitter"
dialog.website_label = "http://code.google.com/p/virtualtwitter"

########## context menu ########################################################
rightMenu = Gtk::Menu.new

menu_item_about = Gtk::ImageMenuItem.new(Gtk::Stock::ABOUT)
menu_item_about.signal_connect("activate") do |widget, event|
    dialog.run
  dialog.destroy
end
rightMenu.append(menu_item_about)

menu_item_quit = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
menu_item_quit.signal_connect("activate") do |widget, event|
    Gtk.main_quit
end
rightMenu.append(menu_item_quit)

########## last tweets menu ####################################################
leftMenu = Gtk::Menu.new
menu_item_tweet = Gtk::MenuItem.new("Searching...")
leftMenu.append(menu_item_tweet)
# Tray icon signals
tray.signal_connect('button_press_event') do |widget, event|
  if event.event_type == Gdk::Event::BUTTON_PRESS
    if event.button == 3   # right mouse button
      rightMenu.show_all
      rightMenu.popup(nil, nil, event.button, event.time)
    end
    if event.button == 1   # left mouse button
      leftMenu.show_all
      leftMenu.popup(nil, nil, event.button, event.time)
    end
  end
end

########## Twitter available? ##################################################
def twitterAvailable?
  urlStr = URI.parse("http://search.twitter.com")
  begin
    Net::HTTP.start(urlStr.host, urlStr.port) do |http|
      return http.head(urlStr.request_uri).code == "200"
    end
  rescue Exception => e
    return false
  end
end

########## start the fun! ######################################################
Thread.abort_on_exception = true
Thread.new {
  #create time to check if the tweets are new
  timeLast = Time.new
  lastFive = []
  #searching tweets
  while true do
    #check if twitter.com is available
    if twitterAvailable?
      searchTerms = []
      #read searchterms.conf
      File.open("#{rootPath}/searchterms.conf") do |file|
        file.each_line do |line|
          searchTerms.push(line)
        end
      end
      #time before the search
      timeBefore = Time.new
      #proceed each searchterm
      searchTerms.each do |searchTerm|
        #search twitter for the given search terms
        Twitter::Search.new(searchTerm).reverse_each do |tweet|
          #when was the tweet created?
          twittertime = tweet.created_at
          tweetTime = Time.parse(twittertime)
          #is it new?
          newer = timeLast <=> tweetTime
          #if newer then create notification and menuitem
          if newer == -1

            #check if tweet is already in list
            #(possible if one tweet includes two searchterms)
            if lastFive.include?(tweet) == false

              #the domain for the profile picture
              urlPath = tweet.profile_image_url[7..22]
              #default image or an individual?
              if urlPath != "s3.amazonaws.com"
                urlPath = "static.twitter.com"
                imagePath = "/images/default_profile_normal.png"
                imageType = "png"
              else
                #create url for the image
                imagePath = tweet.profile_image_url[23..tweet.profile_image_url.length]
                #get the image type
                imageType = tweet.profile_image_url[-3,3]
              end
              #download picture and save to ~/.config/virtualtwitter/
              Net::HTTP.start(urlPath) do |http|
                resp = http.get(imagePath)
                picture = "#{rootPath}/profile_pictures/#{tweet.from_user}.#{imageType}"
                open(picture, "wb") do |file|
                  file.write(resp.body)
                end
              end
              #path to local profile picture
              icon = "#{rootPath}/profile_pictures/#{tweet.from_user}.#{imageType}"

              #create the notification
              connection = Notify.init('virtualtwitter')
              notify = Notify::Notification.new tweet.from_user, tweet.text, icon, nil
              notify.show

              #tweet popup menue should not contain more then 10 elements
              if lastFive.length == 10
                lastFive.delete_at(0)
              end

              #push the actual tweet into the array
              lastFive.push(tweet)

              #create the tweet menue
              leftMenu = Gtk::Menu.new

              #create menu entry for each tweet in lastFive
              lastFive.reverse_each do |tweet|
                #create url for the tweet
                url="http://www.twitter.com/#{tweet.from_user}/status/#{tweet.id}"

                if tweet.text.length <= 70
                  shortText = tweet.text
                else
                  shortText = tweet.text[0..70]
                end

                #trim the text, so that it fits nicely in the menue
                if shortText.length + tweet.from_user.length > 70
                  maxTextLength = shortText.length - tweet.from_user.length
                  if maxTextLength < 70
                    rest = 70 - shortText.length
                    maxTextLength = maxTextLength + rest;
                  end
                  shortText = tweet.text[0..maxTextLength] + "..."
                end

                message = "#{tweet.from_user}:   #{shortText}"
                menu_item_tweet = Gtk::ImageMenuItem.new(message)
                menu_item_tweet.signal_connect("activate") do |widget, event|
                  Launchy.open(url)
                end
                leftMenu.append(menu_item_tweet)

              end #end: create menu entry for each tweet in lastFive
            end #end: check if tweet is already in list
          end #end: if newer then create notification and menuitem
        end #end: search twitter for the given search terms
      end #end: proceed each searchterm

      #time after the query
      timeAfter = Time.new
      #how long did the search last?
      timeBetween = timeAfter - timeBefore
      #when should i treat tweets as new the next round?
      timeLast = Time.now - timeBetween
      #wait 20 seconds and start again
      sleep 30

    else #twitter is not available
      puts "Twitter is not available... will check again in 30 seconds!"
      leftMenu = Gtk::Menu.new
      menu_item_tweet = Gtk::MenuItem.new("Twitter is not available, wait 30 seconds...")
      leftMenu.append(menu_item_tweet)
      sleep 30
    end #end: check if twitter.com is available

  end #end: searching tweets
}

########## show the GUI ########################################################
Gtk.main

